﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SwParaPlatform2.logger
{
    #region 辅助类型
    /// <summary>日志级别</summary>
    public enum LogLevel
    {
        Trace = 0,
        Debug = 1,
        Info = 2,
        Warn = 3,
        Error = 4,
        Fatal = 5
    }

    /// <summary>结构化字段</summary>
    public struct LogField
    {
        public string Key { get; }
        public object Val { get; }
        public LogField(string k, object v) { Key = k; Val = v; }
        public override string ToString() => $"{Key}={Val}";
    }
    #endregion

    /// <summary>
    /// 线程安全 + 异步写入 + 文件轮转 + 目录保留策略 的综合日志系统。<br/>
    /// 日志默认输出到 {DllPath}\logfile\log_yyyyMMdd_HHmmss.txt
    /// </summary>
    public sealed class Logger : IDisposable
    {
        #region 单例
        private static readonly Lazy<Logger> _lazy = new Lazy<Logger>(() => new Logger());
        public static Logger Instance => _lazy.Value;
        #endregion

        #region 配置项（可在 Startup 处修改）
        public LogLevel MinLevel { get; set; } = LogLevel.Debug;      // 最低输出级别
        public long FileSizeLimitBytes { get; set; } = 10 * 1024 * 1024L;   // 10 MB
        public int MaxFileCount { get; set; } = 100;                 // 最多 100 个文件
        public bool WriteToConsole { get; set; } = true;                // 观测性
        #endregion

        #region 私有字段
        private readonly string _dir;
        private StreamWriter _writer;
        private readonly BlockingCollection<string> _queue = new BlockingCollection<string>(new ConcurrentQueue<string>());
        private readonly CancellationTokenSource _cts = new CancellationTokenSource();
        private readonly Task _bgTask;
        private DateTime _createTime;
        // 类字段
        private const string LogPattern = "log_*.yxlog";
        #endregion
        // 公共只读属性，供 UI 访问
        public string LogDirectory => _dir;
        

        #region 构造 & 初始化
        private Logger()
        {
            _dir = Path.Combine(GetDLLPath(), "logfile");
            Directory.CreateDirectory(_dir);
            if (!Directory.Exists(_dir))
                MessageBox.Show($"日志目录 {_dir} 创建失败！");

            // 订阅未处理异常
            AppDomain.CurrentDomain.UnhandledException += (s, e) =>
                Log(LogLevel.Fatal, "UnhandledException", new LogField("Exception", e.ExceptionObject));

            // 启动后台写线程
            _bgTask = Task.Factory.StartNew(WriterLoop,
                                            _cts.Token,
                                            TaskCreationOptions.LongRunning,
                                            TaskScheduler.Default);

            OpenNewLogFile();
            EnforceFileCountLimit();
        }
        #endregion

        #region 核心 API
        /// <summary>写日志（支持可选结构化字段）</summary>
        /* ===== Log(...) 内部 – 写入队列前加密 ===== */
        public void Log(LogLevel lvl, string message, params LogField[] fields)
        {
            if (lvl < MinLevel) return;

            var sb = new StringBuilder()
                .Append('[').Append(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Append("] ")
                .Append(lvl.ToString().ToUpper()).Append(" ")
                .Append(message);

            if (fields?.Length > 0)
                sb.Append(" | ").Append(string.Join(", ", fields.Select(f => f.ToString())));

            string line = sb.ToString();
            //string encrypted = DoubleBase64Encode(line);   // ←← 加密
            string encrypted = line;   // ←← 加密

            _queue.Add(encrypted);                         // 入队异步写入
            /* 观测性输出仍显示明文，方便调试 */
#if DEBUG
            Debug.WriteLine(line);
#endif
            if (WriteToConsole) Console.WriteLine(line);
        }

        /// <summary>快捷重载：默认 Info 级别</summary>
        public void Log(string message, params LogField[] fields) => Log(LogLevel.Info, message, fields);
        #endregion

        #region 文件/目录管理
        private void OpenNewLogFile()
        {
            _writer?.Dispose();
            string fileName = $"log_{DateTime.Now:yyyyMMdd_HHmmss}.yxlog";
            _writer = new StreamWriter(Path.Combine(_dir, fileName), false, Encoding.UTF8) { AutoFlush = true };
            _createTime = DateTime.Now;
        }

        /// <summary>删除多余文件，保留最新 MaxFileCount 个</summary>
        private void EnforceFileCountLimit()
        {
            var files = new DirectoryInfo(_dir)
                        .GetFiles(LogPattern)
                        .OrderBy(f => f.CreationTimeUtc)
                        .ToList();

            while (files.Count >= MaxFileCount)
            {
                try { files.First().Delete(); }
                catch { }
                files.RemoveAt(0);
            }
        }

        /// <summary>检测大小，超限则轮转</summary>
        private void RotateIfNeeded()
        {
            if (_writer.BaseStream.Length < FileSizeLimitBytes) return;
            OpenNewLogFile();
            EnforceFileCountLimit();
        }
        #endregion

        #region 后台写循环
        private void WriterLoop()
        {
            try
            {
                foreach (var line in _queue.GetConsumingEnumerable(_cts.Token))
                {
                    RotateIfNeeded();
                    _writer.WriteLine(line);
                }
            }
            catch (OperationCanceledException) { /* 正常退出 */ }
            finally
            {
                _writer?.Flush();
                _writer?.Dispose();
            }
        }
        #endregion

        #region 杂项
        public string GetDLLPath()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            string assemblyLocation = assembly.Location;
            string directoryPath = Path.GetDirectoryName(assemblyLocation);
            return directoryPath;
        }
        #endregion

        /* ===== 新增工具方法 ===== */
        private static string DoubleBase64Encode(string plain)
        {
            string once = Convert.ToBase64String(Encoding.UTF8.GetBytes(plain));
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(once));
        }
        private static string DoubleBase64Decode(string cipher)
        {
            string once = Encoding.UTF8.GetString(Convert.FromBase64String(cipher));
            return Encoding.UTF8.GetString(Convert.FromBase64String(once));
        }

        /* ===== 新增静态接口：解密整个日志文件 ===== */
        public static string DecryptLogFile(string encodedPath)
        {
            if (!File.Exists(encodedPath))
                throw new FileNotFoundException("日志文件不存在", encodedPath);

            string decodedPath = Path.Combine(
                Path.GetDirectoryName(encodedPath) ?? "",
                Path.GetFileNameWithoutExtension(encodedPath) + "_decoded.txt");   // 保持 txt，便于直接查看

            /* 传统 using 语法，C# 7.3 完全支持 */
            using (StreamReader sr = new StreamReader(encodedPath, Encoding.UTF8))
            {
                using (StreamWriter sw = new StreamWriter(decodedPath, false, Encoding.UTF8))
                {
                    string line;                          // 非 nullable
                    while ((line = sr.ReadLine()) != null)
                    {
                        try
                        {
                            sw.WriteLine(DoubleBase64Decode(line));
                        }
                        catch
                        {
                            // 单行损坏时忽略，也可记录到错误日志
                        }
                    }
                }
            }
            // --- 新增：用系统默认编辑器打开解密后的明文 ---
            try { Process.Start(decodedPath); }   // 在 Win10+/Win11 默认用记事本或关联程序
            catch { /* 某些环境可能禁用 Shell 执行，可忽略或记录 */ }
            return decodedPath;   // 方便在 UI 中提示
        }


        /// <summary>
        /// 弹出文件对话框，用户选择 .yxlog 后立即解密到同目录。
        /// 可放到 WinForms 按钮事件里直接调用： Logger.Instance.ShowDecodeDialog(this);
        /// </summary>
        /// <param name="owner">WinForms 窗体；控制台程序可传 null</param>
        public void ShowDecodeDialog(IWin32Window owner = null)
        {
            using (var dlg = new OpenFileDialog())
            {
                dlg.Title = "选择需要解密的日志文件";
                dlg.Filter = "日志文件 (*.yxlog)|log_*.yxlog|所有文件|*.*";
                dlg.InitialDirectory = LogDirectory;
                dlg.RestoreDirectory = true;

                if (dlg.ShowDialog(owner) == DialogResult.OK)
                {
                    try
                    {
                        string newFile = DecryptLogFile(dlg.FileName);
                        MessageBox.Show(owner,
                                        $"✅ 解密完成：\n{newFile}",
                                        "成功",
                                        MessageBoxButtons.OK,
                                        MessageBoxIcon.Information);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(owner,
                                        $"❌ 解密失败：{ex.Message}",
                                        "错误",
                                        MessageBoxButtons.OK,
                                        MessageBoxIcon.Error);
                    }
                }
            }
        }

        #region 释放
        public void Dispose()
        {
            _cts.Cancel();
            _queue.CompleteAdding();
            try { _bgTask.Wait(2000); }  // 等待后台线程结束
            catch { }
            _cts.Dispose();
        }
        #endregion
    }
}
